package we3d.material 
{
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.Sprite;
	import flash.display.BlendMode;
	import flash.utils.Dictionary;
	import we3d.rasterizer.IRasterizer;
	import we3d.rasterizer.ScanlineWire;
	import we3d.rasterizer.ScanlineFlat;
	import we3d.rasterizer.ScanlineFlatZB;
	import we3d.rasterizer.ScanlineFlatLight;
	import we3d.rasterizer.ScanlineFlatLightZB;
	import we3d.rasterizer.ScanlineTX;
	import we3d.rasterizer.ScanlineTX32;
	import we3d.rasterizer.ScanlineTXZB;
	import we3d.rasterizer.ScanlineTXZB32;
	import we3d.rasterizer.NativeWire;
	import we3d.rasterizer.NativeFlat;
	import we3d.rasterizer.NativeFlatWire;
	import we3d.rasterizer.NativeFlatLight;
	import we3d.rasterizer.NativeFlatWireLight;
	import we3d.rasterizer.NativeTX;
	import we3d.filter.ZBuffer;
	import we3d.scene.LightGlobals;
	
	/**
	* The MaterialManager class is a helper to create and setup Materials
	*/
	public class MaterialManager 
	{
		public function MaterialManager () {}
		
		/**
		* Contains all materials created with createMaterial()
		*/
		public var materials:Array = [];
		
		private static var scanlineWire:		ScanlineWire 		= new ScanlineWire;
		private static var scanlineFlat:		ScanlineFlat 		= new ScanlineFlat;
		private static var scanlineFlatLight:	ScanlineFlatLight 	= new ScanlineFlatLight;
		private static var scanlineTX:			ScanlineTX 			= new ScanlineTX;
		private static var scanlineTX32:		ScanlineTX32 		= new ScanlineTX32;
		private static var nativeFlat:			NativeFlat 			= new NativeFlat;
		private static var nativeWire:			NativeWire 			= new NativeWire;
		private static var nativeFlatWire:		NativeFlatWire 		= new NativeFlatWire;
		private static var nativeFlatLight:		NativeFlatLight 	= new NativeFlatLight;
		private static var nativeFlatWireLight:	NativeFlatWireLight = new NativeFlatWireLight;
		private static var nativeTX:			NativeTX 			= new NativeTX;
		
		private var materialsByName:Dictionary = new Dictionary(true);
		
		/**
		* Returns a material in the material list
		*/
		public function getMaterialAt (id:int) :Surface {
			return materials[id];
		}
		/**
		* Returns a material by name if it had a name property
		*/
		public function getMaterialByName (name:String) :Surface {
			return materialsByName[name];
		}
		/**
		* Creates a material and controls the appearance with the propObj
		* 
		* <code><pre>
		* 
		* propObj {
		* 
		* 
		*  // General Properties
		* 
		*  .name				if set, the material is also stored by name
		* 
		*  .id					if set to a number, the material is inserted at this index of the material list
		* 
		* 
		* 
		*  // Default Surface Properties
		* 
		*  .color				the color of the material
		* 
		*  .alpha				alpha transparency (0-1)
		* 
		*  .lineStyle			the thickness of outlines in pixel
		* 
		*  .lineColor			the line color if polygons should have outlines
		* 
		*  .lineAlpha			alpha of outlines (0-1)
		* 
		* 
		* 
		*  // Lighting properties
		* 
		*   .lighting			true if the material can use lighting
		* 
		*  .lightGlobals		if lighting is true lightGlobals have to be a LightGlobals instance
		* 
		*  .luminosity			lighting property for self lighting (0-1 or higher)
		* 
		*  .diffuse				lighting property for diffuse light reflection (0-1 or higher)
		* 
		* 
		* 
		* 	// To use Texturing, set bitmap to one of the bitmap types
		* 
		* 	.bitmap				Bitmap, BitmapData or Sprite; currently lighting and outlines are not supported with bitmap textures
		*   
		*   .transparent		If transparent is set the material uses a 32 Bit texture rasterizer
		* 
		* 
		* 
		*  // The following properties control the Rasterizer used in the Material
		* 
		*  .scanline			if true, polygons with the new material are rendered with one of the AS3 software rasterizers,
		* 						if scanline is unset or false, the native rasterizer of the flash player is used
		* 
		*  .zbuffer				to enable depth sorting with pixel precision, set the zbuffer to a ZBuffer filter, 
		* 						ZBuffer can only be used with scanline and bitmap enabled, a zbuffer for flat shading and lighting is still work in progress
		* 
		* 
		* 
		* }
		* </pre></code>
		* 
		* @param	propObj
		* @return
		*/
		public function createMaterial (propObj:Object) :Surface {
			
			var sf:Surface = new Surface();
			
			setupMaterial(sf, propObj);
			
			if(typeof(propObj.id)=="number") {
				materials.splice(propObj.id, 0, sf);
			}else{
				materials.push(sf);
			}
			
			return sf;
		}
		/**
		* Controls the appearance of a material
		* @param	sf
		* @param	propObj
		*/
		public function setupMaterial (sf:Surface, propObj:Object) :void {
			
			if(propObj.name) materialsByName[propObj.name] = sf;
			if(propObj.hideBackfaces != null) sf.hideBackfaces = propObj.hideBackfaces;
			
			if(propObj.bitmap) 
			{
				var bmpatb:BitmapAttributes = new BitmapAttributes();
				sf.attributes = bmpatb;
				
				if(propObj.bitmap is Bitmap) {
					bmpatb.texture = propObj.bitmap.bitmapData;
				}else if(propObj.bitmap is BitmapData) {
					bmpatb.texture = propObj.bitmap;
				}else if(propObj.bitmap is Sprite) {	// cast sprite to bitmap
					var sp:Sprite = propObj.bitmap;
					var bmp:BitmapData = new BitmapData(sp.width, sp.height, true, 0);
					bmp.draw(sp);
					bmpatb.texture = bmp;
				}
				
				if(!propObj.scanline && !propObj.zbuffer) {
					sf.rasterizer = nativeTX;
				}else{
					if(propObj.zbuffer) {
						if(propObj.zbuffer is ZBuffer) {
							if(propObj.transparent) {
								sf.rasterizer = new ScanlineTXZB32(propObj.zbuffer);
							}else{
								sf.rasterizer = new ScanlineTXZB(propObj.zbuffer);
							}
						}else{
							trace("** MaterialManager Error ** 'zbuffer' have to be a instance of we3d.filter.ZBuffer");
							if(propObj.transparent) {
								sf.rasterizer = scanlineTX32;
							}else{
								sf.rasterizer = scanlineTX;
							}
						}
					}else{
						if(propObj.transparent) {
							sf.rasterizer = scanlineTX32;
						}else{
							sf.rasterizer = scanlineTX;
						}
					}
				}
				
			}
			else
			{
				
				var w:Boolean=false;
				var f:Boolean=false;
				
				var atb:FlatWireLightAttributes = new FlatWireLightAttributes();
				
				if(propObj.color) {
					atb.color = propObj.color;
					f = true;
				}
				if(propObj.alpha) {
					atb.alpha = propObj.alpha;
					f = true;
				}
				
				if(propObj.lineColor) {
					atb.lineColor = propObj.lineColor;
					w = true;
				}
				if(propObj.lineStyle) {
					atb.lineStyle = propObj.lineStyle;
					w = true;
				}
				if(propObj.lineAlpha) {
					atb.lineAlpha = propObj.lineAlpha;
					w = true;
				}
				
				if(!propObj.scanline) {
					if(w) {
						if(f) {
							if(propObj.lighting && propObj.lightGlobals is LightGlobals) {
								if(propObj.luminosity) atb.luminosity = propObj.luminosity;
								if(propObj.diffuse) atb.diffuse = propObj.diffuse;
								if(propObj.lightGlobals) atb.lightGlobals = propObj.lightGlobals;
								sf.rasterizer = nativeFlatWireLight;
								sf.attributes = atb;
							}else{
								sf.attributes = new FlatWireAttributes(atb.color, atb.alpha, atb.lineColor, atb.lineStyle, atb.lineAlpha);
								sf.rasterizer = nativeFlatWire;
							}
						}else{
							sf.attributes = new WireAttributes(atb.lineColor, atb.lineAlpha, atb.lineStyle);
							sf.rasterizer = nativeWire;
						}
					}
					else{
						if(propObj.lighting && propObj.lightGlobals is LightGlobals) {
							if(propObj.luminosity) atb.luminosity = propObj.luminosity;
							if(propObj.diffuse) atb.diffuse = propObj.diffuse;
							if(propObj.lightGlobals) atb.lightGlobals = propObj.lightGlobals;
							sf.attributes = new FlatLightAttributes(atb.color, atb.alpha, atb.luminosity, atb.diffuse, propObj.lightGlobals || null);
							sf.rasterizer = nativeFlatLight;
						}else{
							sf.attributes = new FlatAttributes(atb.color, atb.alpha);
							sf.rasterizer = nativeFlat;
						}
					}
				}
				else{
					if(w) {
						if(f) {// scanline flatwire
							if(propObj.lighting && propObj.lightGlobals is LightGlobals) {
								trace("** MaterialManager Warning ** Scanline rasterizer do not support outlines");
								// TODO sf.rasterizer = scanlineFlatWireLight;
								sf.attributes = new FlatLightAttributes(atb.color, atb.alpha, atb.luminosity, atb.diffuse, propObj.lightGlobals || null);
								sf.rasterizer = scanlineFlatLight;
							}else{
								trace("** MaterialManager Warning ** Scanline rasterizer do not support outlines");
								// TODO sf.rasterizer = scanlineFlatWire;
								sf.attributes = new FlatAttributes(atb.color, atb.alpha);
								sf.rasterizer = scanlineFlat;
							}
						}else{// scanline wire
							sf.attributes = new WireAttributes(atb.color, atb.alpha);
							sf.rasterizer = scanlineWire;
						}
					}
					else
					{
						if(propObj.lighting && propObj.lightGlobals is LightGlobals) {
							
							if(propObj.zbuffer) {
								if(propObj.zbuffer is ZBuffer) {
									sf.attributes = new FlatLightAttributes(atb.color, atb.alpha, atb.luminosity, atb.diffuse, propObj.lightGlobals || null);
									sf.rasterizer = new ScanlineFlatLightZB(propObj.zbuffer);
								}else{
									trace("** MaterialManager Error ** 'zbuffer' have to be a instance of we3d.filter.ZBuffer");
									sf.attributes = new FlatLightAttributes(atb.color, atb.alpha, atb.luminosity, atb.diffuse, propObj.lightGlobals || null);
									sf.rasterizer = scanlineFlatLight;
								}
							}else{
								sf.attributes = new FlatLightAttributes(atb.color, atb.alpha, atb.luminosity, atb.diffuse, propObj.lightGlobals || null);
								sf.rasterizer = scanlineFlatLight;
							}
							
						}else{
							if(propObj.zbuffer) {
								if(propObj.zbuffer is ZBuffer) {
									sf.attributes = new FlatAttributes(atb.color, atb.alpha);
									sf.rasterizer = new ScanlineFlatZB(propObj.zbuffer);
								}else{
									trace("** MaterialManager Error ** 'zbuffer' have to be a instance of we3d.filter.ZBuffer");
									sf.attributes = new FlatAttributes(atb.color, atb.alpha);
									sf.rasterizer = scanlineFlat;
								}
							}else{
								sf.attributes = new FlatAttributes(atb.color, atb.alpha);
								sf.rasterizer = scanlineFlat;
							}
						}
						
					}
				}
			}
		}
		/**
		* Removes one ore more materials from the material list
		* @param	id	the id of the first material to remove
		* @param	count	the count of materials to delete
		*/
		public function removeMaterialAt (id:uint, count:uint=1) :void {
			if(id >= 0 && id<=materials.length-count) {
				materials.splice(id, count);
			}
		}
		/**
		* Remove a material by name
		*/
		public function removeMaterialByName (name:String) :Boolean {
			var mat:* = materialsByName[name];
			if(mat) {
				var id:int = materials.indexOf(mat);
				if(id != -1) {
					materials.splice(id, 1);
					delete materialsByName[name];
					return true;
				}
			}
			return false;
		}
		
	}
}